extends Chara

var utils = globalData.infoDs["g_azurlaneUtils"]
var azurCtrl = globalData.infoDs["g_azurlane"]
var base = globalData.infoDs["g_azurlaneBase"]
var buff = globalData.infoDs["g_azurlaneBuffs"]
var plus = globalData.infoDs.get("g_azurlanePlus")
var live = globalData.infoDs.get("g_azurlaneLive")
var omgCtrl = globalData.infoDs["g_azurlaneEvo"]
var gSys = globalData.infoDs["g_azurlaneSys"]
var relic = globalData.infoDs["g_azurlaneRelic"]
var itemSkill = globalData.infoDs["g_azurlaneItemSkills"]
var skillPool = globalData.infoDs["g_azurlaneSkills"]
var research = globalData.infoDs["g_azurlaneResearch"]

var type = sys.CharaType.NORMAL
var tag = "azurlane"

var net = azurCtrl.net

var pic = null
#英文名称,用于立绘检索
var ename

#角色唯一ID
var rndId:int

var level:int = 1

#额外技能槽
var skillSlot = null
#阵亡次数
var deathNum = 0
#角色永久额外属性区
var attEx = utils.loadScript("AzurAtt")
#最大装备上限
var maxItem = 3
#角色击杀数
var killNum = 0

var attInfox = utils.loadScript("AzurAtt")
var attTurn = utils.loadScript("AzurAtt")

#阵营
var camp = null
#是否允许施放技能
var enableCastSkill = true
#是否拥有动态立绘
var hasAnime = false
#拖拽动画帧数
var animeFrame1 = 5
#待机动画帧数
var animeFrame2 = 6

signal upS
signal normalAtkChara
signal onBattleStart
signal onBattleEnd
#角色濒死时发出此信号，延迟50ms后进行检测，检测通过则继续进行死亡流程
signal onHurtDeath

func _ready():
	att = utils.loadScript("AzurAtt")
	delAllAtt()
	att.maxHp = 30
	att.hp = att.maxHp
	att.atkRan = 3
	attInfox.maxHp = 10
	addAtt(attEx)
	addAtt(attInfox)
	addAtt(attTurn)

	evos = chaData.getEvoIds(id)
	infoUp()

	if sys.main != null:
		sys.main.connect("process", self, "process")
		.set_process(false)
		sys.main.connect("preCharaCastCdSkill", self, "_preCharaCastCdSkill")
		sys.main.connect("onCharaDeathWord", self, "_onCharaDeathWord")

func addAtt(att):
	if att != null:
		atts.append(att)
		att.connect("onChange", self, "changeAtt")
		upAtt()

func delAllAtt():
	for i in atts:
		delAtt(i)

func delAtt(att):
	if att != null:
		atts.erase(att)
		if att.is_connected("onChange", self, "changeAtt"):att.disconnect("onChange", self, "changeAtt")
		upAtt()

func upAtt(all = true):
	if not att.has_method("set_air"):return
	var hpL = 1
	if att.maxHp > 0:
		hpL = att.hp / att.maxHp
	att.clear()
	att.spd = 1.0
	for i in atts:
		if not i.has_method("set_air"):continue
		att.plus(i)
	for i in config.attRdsKeys:
		att.info[config.attRds[i]] *= 1 + att.info[i]

	att.info["gun"] = att.info["atk"] * 0.01
	att.info["torpedo"] = att.info["mgiAtk"] * 0.01

	#属性最小值判定
	for i in att.info.keys():
		var at = att.get(i)
		var minAt = attEx.minLimit.get(i)
		if at < minAt:
			att.set(i, minAt)
	att.hp = att.maxHp * hpL
	isUpAtt = false
	

#覆写原版函数
func newChara(id, cell):
	var cells = utils.getAllCells(team)
	cells.shuffle()	
	var vs = [Vector2(0, 0), Vector2(1, 0), Vector2( - 1, 0), Vector2(0, 1), Vector2(0, - 1), Vector2(1, 1), Vector2( - 1, 1), Vector2( - 1, - 1), Vector2(1, - 1)]
	var cl = null
	for i in vs:
		var lc = i + cell
		if matCha(lc) == null:
			cl = lc
			break
	if cl == null or not sys.main.isMatin(cl) or matCha(cl) != null:
		#召唤失败，尝试在其他位置重新召唤
		for i in cells:
			if matCha(i) == null:
				cl = i
				break
	if cl != null and sys.main.isMatin(cl) and matCha(cl) == null:
		var cha = sys.main.newChara(id, team)
		sys.main.map.add_child(cha)
		sys.main.setMatCha(cl, cha)
		cha.position = sys.main.map.map_to_world(cl)
		cha.isSumm = true
		cha.newEff("sk_zhao")
		emit_signal("onNewChara", cha)
		sys.main.emit_signal("onCharaNewChara", cha)
		return cha

class AzurSkill:
	var enable = true
	var id = ""
	var nowTime = 0
	var cd = 0

class AttackInfo:
	extends AtkInfo
	#伤害名称
	var skill
	#伤害倍率
	var factor = 1
	#穿甲倍率
	var penL = 0
	#是否不产生实际伤害
	var virtual = false
	#被攻击角色，是否死亡，用于濒死信号检测
	var hurtChaDead = false
	#是否必定暴击
	var mustCri = false
	#结算各种增减伤后应该造成的伤害
	var middleDmg = 0
	#最终实际伤害，含护盾伤害
	var finalDmg = 0
	#是否无视机动
	var ignoreDod = false
	#反伤系数，默认为0
	var fanShang = 0

	func _init():
		._init()
		pass

func azurHurtChara(cha, dmg, hurtType, atkType, from = null, canCri = false, factor = 1):
	if cha == null or cha.isDeath:return
	pause(0.15, false)
	hitHua = true
	var atkInfo = AttackInfo.new()
	atkInfo.skill = from
	atkInfo.rate = 1
	atkInfo.hitCha = cha
	atkInfo.atkCha = self
	atkInfo.hitObj = cha
	atkInfo.atkObj = self
	atkInfo.hurtType = hurtType
	atkInfo.hurtVal = dmg
	atkInfo.atkType = atkType
	atkInfo.atkVal = dmg
	atkInfo.factor = factor
	atkInfo.canCri = canCri
	if atkType == sys.AtkType.GUN:
		atkInfo.isAtk = true
	else:
		atkInfo.isAtk = false
	atkInfo.isMiss = false
	cha.hurtArit(atkInfo)

func atkRun(cha):
	atkInfo.atkCha = masCha
	atkInfo.atkObj = self
	atkInfo.hitCha = cha.masCha
	atkInfo.hitObj = cha
	masCha._atkIng(atkInfo)
	cha._hit(atkInfo)
	cha.emit_signal("onHit", atkInfo)
	self.pause(0.1)
	atkIng = true

func _hit(atkInfo:AtkInfo):
	pause(0.15, false)
	atkInfo.isCri = false
	atkInfo.isMiss = false
	hitHua = true
	hurtArit(atkInfo)

func hurtArit(atkInfo):
	if atkInfo.atkCha.get("tag") != "azurlane":
		.hurtArit(atkInfo)
		return
	if isDeath or isItem:return 
	if hurtNum > 10:return
	checkDeath(atkInfo)
	if atkInfo.get("factor") == null:
		var attack = AttackInfo.new()
		var skip_variables = ["Script Variables", "script"]
		for i in atkInfo.get_property_list():
			var key = i.name
			if not key in skip_variables and key != "":
				attack.set(key, atkInfo.get(key))
		atkInfo = attack
	emit_signal("onAtkInfo", atkInfo)
	_onAtkInfo(atkInfo)
	hurtNum += 1
	if not atkInfo.atkCha.isItem:
		atkInfo.atkCha.emit_signal("onAtkInfo", atkInfo)
		atkInfo.atkCha._onAtkInfo(atkInfo)
	#计算雷击
	if atkInfo.atkType == sys.AtkType.TORPEDO:
		atkInfo.hurtVal *= (1 + atkInfo.atkCha.att.torpedo)
	#计算护甲
	if atkInfo.hurtType == sys.HurtType.WEAPON:
		var defVal = clamp(att.def * (1 - atkInfo.penL - atkInfo.atkCha.att.penL) - atkInfo.atkCha.att.pen, 0, 10000)
		atkInfo.hurtVal = atkInfo.hurtVal * (100.0 / (100 + defVal))
	if atkInfo.canCri and randf() < atkInfo.atkCha.att.cri or atkInfo.mustCri:
		atkInfo.hurtVal *= 2 * (1 + atkInfo.atkCha.att.criR)
		atkInfo.isCri = true
	#计算机动   0-0    50-0.33  100-0.5 200-0.66
	var dodRate = 1 - 100.0 / (100 + att.dod)
	if atkInfo.atkType == sys.AtkType.GUN and not atkInfo.ignoreDod and randf() < dodRate/2:
		atkInfo.isMiss = true
	elif atkInfo.atkType == sys.AtkType.TORPEDO and not atkInfo.ignoreDod and randf() < dodRate:
		atkInfo.isMiss = true

	
	if not isItem:
		atkInfo.atkCha.emit_signal("onAtkChara", atkInfo)
		atkInfo.atkCha._onAtkChara(atkInfo)

	if not isItem:
		emit_signal("onHurt", atkInfo)
		_onHurt(atkInfo)

	atkInfo.hurtVal *= (atkInfo.atkCha.att.atkR + atkInfo.factor)
	atkInfo.hurtVal *= (1 - att.defR)
	#去掉负伤害
	if atkInfo.hurtVal <= 0:return

	atkInfo.middleDmg = atkInfo.hurtVal

	if atkInfo.virtual or isDeath:return
	var realDmg = atkInfo.hurtVal
	var shieldDmg = 0
	if not atkInfo.isMiss:
		#处理护盾
		if shield > 0:
			shieldDmg = min(shield, atkInfo.hurtVal)
			changeShield(-shieldDmg)
			if shield <= 0:
				realDmg = atkInfo.hurtVal - shieldDmg
			else:
				realDmg = 0
		if realDmg >= att.hp:
			realDmg = att.hp
			atkInfo.hurtChaDead = true
			emit_signal("onHurtDeath", atkInfo)
			if atkInfo.hurtChaDead:
				onHurtDeath(atkInfo)
			if not atkInfo.hurtChaDead:
				return
		atkInfo.hurtVal = realDmg
		att.hp -= atkInfo.hurtVal
		atkInfo.finalDmg = realDmg + shieldDmg
		emit_signal("onHurtEnd", atkInfo)
		onHurtEnd(atkInfo)

	atkInfo.canCri = false
	if atkInfo.atkType == sys.AtkType.GUN and atkInfo.atkCha.isSumm and atkInfo.atkCha.att.atkRan == 1:
		self.aiCha = atkInfo.atkCha
	#处理吸血
	if atkInfo.atkCha.att.reHp > -0.9 and not atkInfo.isMiss and atkInfo.atkCha.att.suck > 0:
		atkInfo.atkCha.healCha(atkInfo.atkCha, atkInfo.hurtVal * atkInfo.atkCha.att.suck, false, 0.03)
	
	if sys.main.isNumVis:
		var node = sys.newEff("numHit", position + sprcPos, false, 1)
		node.z_index = 51;
		node.init(realDmg + shieldDmg, self)
		if atkInfo.isMiss:node.play("miss")
		elif atkInfo.atkType == sys.AtkType.GUN:
			node.play("wu")
		elif atkInfo.hurtType == sys.HurtType.REAL:
			node.play("zhen")
		else:
			node.play("mo")
		if atkInfo.isCri:
			node.lab.text += "!"
	checkDeath(atkInfo)
	atkInfo.atkCha.onAtkEnd(atkInfo)

func onHurtEnd(atkInfo):
	pass

func checkDeath(atkInfo):
	if att.hp <= 0.0 and isDeath == false:
		att.hp = 0
		isDeath = true
		emit_signal("onDeath", atkInfo)
		_onDeath(atkInfo)
		if isDeath:
			atkInfo.atkCha.emit_signal("onKillChara", atkInfo)
			atkInfo.atkCha._onKillChara(atkInfo)
			sys.main.emit_signal("onCharaDel", self)
			sys.delEff(position, dire)
			del()	

func onAtkEnd(atkInfo):
	if atkInfo.finalDmg > 0 and not isDeath and not atkInfo.hitCha.isDeath and atkInfo.atkType != sys.AtkType.EFF and atkInfo.fanShang > 0:
		atkInfo.hitCha.azurHurtChara(self, atkInfo.finalDmg*atkInfo.fanShang, sys.HurtType.REAL, sys.AtkType.EFF, "反伤")

func onHurtDeath(atkInfo):
	pass

#变更护盾值
var shield:int = 0
var shieldLimit = null
func changeShield(num):
	if num > 0:
		num *= (att.reHp + 1)
		num *= (relic.shieldBonus)
	shield += num
	if shieldLimit == null and shield > att.maxHp:
		shield = att.maxHp
	elif shieldLimit != null and shield > shieldLimit:
		shield = shieldLimit
	shield = max(0, int(shield))

	tpView.set_value(shield)
	if shieldLimit == null:
		tpView.set_max(att.maxHp)
	else:
		tpView.set_max(shieldLimit)

func plusShield(num):
	if num < 0 and team == 1:
		num *= -1
	changeShield(num)
	
#护盾条相关
var tpView
#护盾条初始化
func tpViewInit():
	if isSumm:return
	tpView = TextureProgress.new()
	var imt = utils.createImageTexture("icons/shield.png")
	tpView.set_progress_texture(imt)
	tpView.set_fill_mode(4)
	tpView.set_max(att.maxHp)
	tpView.set_step(0.01)
	tpView.set_min(0)
	tpView.set_value(0)
	tpView.set_position(Vector2(-63, -25))
	self.get_node("ui").add_child(tpView)

signal healCha
#治疗并统计
func healCha(cha, num, cri = false, rate = 1):
	if cri and sys.rndPer(att.cri * 100):
		num *= 2 + att.criR

	var shieldNum = 0
	if cha.has_method("changeShield"):
		if cha.att.hp >= cha.att.maxHp:
			shieldNum = num
		elif cha.att.hp + num >= cha.att.maxHp:
			shieldNum = num - (cha.att.maxHp - cha.att.hp)
		cha.changeShield(shieldNum * rate)	
	if cha.team == 2 or num > 0:
		num = min(cha.att.maxHp - cha.att.hp, num)
		cha.plusHp(num)
	if num + shieldNum > 0 and not isSumm:
		call("increDealNum", num + shieldNum)
		emit_signal("healCha", cha)

#角色上下场会触发infoUp
func infoUp():
	skillStrs.clear()
	skills.clear()
	_info()
	_extInit()
	call("postInit")
	call("postInfoUp")
	if isItem == false:
		pass
	if id != "":
		var im = Image.new()
		im.load("%s/%s/cha.png" % [chaData.infoDs[id].dir, id])
		var imt = ImageTexture.new()
		imt.create_from_image(im)
		imt.flags = 4
		img.texture = imt
		img.margin_top = - img.texture.get_height() + 35
		img.margin_left = - img.texture.get_width() / 2
		$ui / chaName.text = chaName
		$ui / chaName.modulate = Color(config.itemGradeColors[lv - 1])
		sprcPos = Vector2(0, - img.texture.get_height() / 2)
		if not isItem:
			$ui / chaName.hide()
		else :
			$ui / chaName.show()

		loadImgNew()	
	var lvL = 1 + (lv - 1) * 1
	img.rect_pivot_offset = Vector2(img.texture.get_width() * 0.5, img.texture.get_height())
	upAtt()	

var animatedSprite = null

var dragTexture = null
var normalTexture = null
func loadImgNew():
	if hasAnime and dragTexture == null:
		utils.changeScript(img, gSys.spritScript)
		dragTexture = AnimatedTexture.new()
		dragTexture.frames = animeFrame1
		dragTexture.fps = 8

		if live.animeCache.has(ename):
			var pics = live.animeCache.get(ename)
			for i in range(animeFrame1):
				dragTexture.set_frame_texture(i, pics[i])
		else:
			for i in range(animeFrame1):
				var img = base.loadImg("%s" % [chaData.infoDs[id].dir], "%d.png"%[i+1])
				dragTexture.set_frame_texture(i, img)
				utils.dicAppend(live.animeCache, ename, img)

		normalTexture = AnimatedTexture.new()
		normalTexture.frames = animeFrame2
		normalTexture.fps = 4

		if live.animeCache.has(id):
			var pics = live.animeCache.get(id)
			for i in range(animeFrame2):
				normalTexture.set_frame_texture(i, pics[i])
		else:
			for i in range(animeFrame2):
				var img = base.loadImg("%s/%s" % [chaData.infoDs[id].dir, id], "%d.png"%[i+1])
				normalTexture.set_frame_texture(i, img)
				utils.dicAppend(live.animeCache, id, img)

	if normalTexture != null:
		img.texture = normalTexture

func _on_Sprite_pressed():
	var s = getInfo()
	var msg = sys.newMsg("charaInfoMsg")
	utils.changeScript(msg, gSys.charaInfoMsgScript)
	msg.init(self, s)
	emit_signal("onPressed", self)
	call("chaInfo", self)

func addItem(item):
	if item != null and item.type == config.EQUITYPE_EQUI and (items.size() < maxItem or utils.isExtraItem(item)):
		addBuff(item)
		if item.utils == null:
			item.initVars()
		items.append(item)
		emit_signal("onAddItem", item)
		return true
	return false

func hurtChara(cha, val, hurtType = sys.HurtType.WEAPON, atkType = sys.AtkType.SKILL):
	var af = AttackInfo.new()
	af.atkVal = val
	af.atkType = atkType
	af.hurtType = hurtType
	af.atkCha = self
	af.hitCha = cha
	cha._hit(af)

func normalAtkChara(cha):
	if cha == null or cha.isDeath:return
	if mainFlag:
		emit_signal("normalAtkChara", cha)
		doNormalAtk(cha)

func normalAtk(cha):
	if mainFlag:
		doNormalAtk(cha)
#弹道速度
var atkFlySpd = 1000
func doNormalAtk(cha):
	var d:Eff = newEff(atkEff, sprcPos)
	d._initFlyCha(cha, atkFlySpd)
	_onNormalAtk(cha)
	yield (d, "onReach")
	if sys.isClass(cha, "Chara"):
		atkInfo.atkCha = self
		atkInfo.rate = 1
		atkInfo.isCri = false
		atkInfo.canCri = true
		atkInfo.atkVal = mainDmg * (1 + att.gun)
		atkInfo.hurtVal = atkInfo.atkVal
		atkInfo.hurtType = sys.HurtType.WEAPON
		atkInfo.atkType = sys.AtkType.GUN
		atkRun(cha)

func addBuff(buff):
	if buff != null:
		buff.masCha = self
		var b = hasBuff(buff.id)
		if b != null and not (b is Item) and b.life != null:
			if buff.life == null:return buff
			b.life += buff.life
			if b.life > 20:b.life = 20
			emit_signal("onAddBuff", buff)
			if sys.main != null:
				sys.main.emit_signal("onCharaAddBuff", buff)
			_onCharaAddBuff(buff)
			if isItem == false:_onAddBuff(buff)
			return b
		else:
			buff.setCha(self, buff.casCha)
			buffs.append(buff)
			if buff.get("att") != null and buff.att.has_method("get_gun"):
				addAtt(buff.att)
			emit_signal("onAddBuff", buff)
			if sys.main != null:
				sys.main.emit_signal("onCharaAddBuff", buff)
			_onCharaAddBuff(buff)
			if isItem == false:_onAddBuff(buff)
			return buff
#复写原版函数
func delItem(item):
	if item != null:
		item.onDelCha()
		delBuff(item)
		items.erase(item)
		item.masCha = null
		emit_signal("onDelItem", item)
#复写原版函数
func delBuff(buff):
	if buff != null:
		buffs.erase(buff)
		if buff.att != null:
			delAtt(buff.att)
		buff.del()
#复写原版函数
func aiSeekCha(exCha = null):
	var minl = 1000
	for x in range(sys.main.matW):
		for y in range(sys.main.matH):
			var i = sys.main.mat[x][y]
			if i != null and i != exCha and i != self and i.team != team and not i.isDeath and not i.isItem and i.att.hp > 0:
				var l = cellRan(i.cell)
				if l < minl:
					setAiCha(i)
					minl = l
#复写原版函数
func act(var name, var inX = 0, var inY = 0):
	if isDeath:return 
	if name == "move" and actLv == 0 and not isMoveIng:
		setDire(inX)
		play("move")
		setCell(Vector2(inX, inY))
	elif name == "idle" and nowAnim == "move" and actLv == 0:
		if nowAnim == "idle":
			move()
		else :
			play(name)
	elif name == "atk" and actLv < 10:
		setDire(inX)
		if inY > 0:play("atkDown", 11, 0, att.spd)
		if inY < 0:play("atkUp", 11, 0, att.spd)
		else :play("atk", 11, 0, att.spd)
#复写原版函数
func _ai(delta):
	if not sys.isClass(aiCha, "Chara") or aiCha.isDeath or aiCha.att.hp <= 0:
		aiSeekCha()
		return 
	var vpos = Vector2(aiCha.cell.x - cell.x, aiCha.cell.y - cell.y)
	var l = cellRan(aiCha.cell)
	if l > 1000:return 
	atime -= delta
	if l <= att.atkRan:
		act("atk", vpos.x, vpos.y)
	elif not isMoveIng and atime <= 0:
		sys.main.setMatCha(aiCha.cell, null)
		var ps = sys.main.aStar.get_point_path(sys.main.cellToId(cell), sys.main.cellToId(aiCha.cell))
		sys.main.setMatCha(aiCha.cell, aiCha)

		if ename == "lingbo":
			print(ps)
			print(sys.main.aStar.get_available_point_id())

		if ps.size() > 1:
			act("move", ps[1].x, ps[1].y)
		else:
			if vpos.x > 0:
				vpos.x = 1;
			elif vpos.x < 0:vpos.x = - 1
			if vpos.y > 0:
				vpos.y = 1;
			elif vpos.y < 0:vpos.y = - 1
			if vpos.x != 0:
				vpos.y = 0
			vpos += cell
			if matCha(vpos) == null:
				act("move", vpos.x, vpos.y)
			else:
				aiSeekCha(aiCha)
				if atime <= 0:atime = 0.1

###########################舰装相关###########################
var mainDmg = 0		#主炮基础伤害
var mainName = ""	#主炮名称
var mainTxt = ""	#主炮特效
var mainSk = null	#主炮技能
var mainFlag = true #能否装备主炮
var mainId = ""

var torpDmg = 0		#鱼雷基础伤害
var torpSk = null	#鱼雷技能
var torpCd = 0		#鱼雷冷却
var torpFlag = true #能否装备鱼雷
var torpName = ""	#鱼雷名称
var torpTxt = ""	#鱼雷特效
var torpId = ""

class AzurBuff:
	extends Buff
	var utils = globalData.infoDs["g_azurlaneUtils"]
	#buff强度， 1：弱强度，2：高强度
	var dispel = 1
	var buffName = ""
	func attInit():
		att = utils.loadScript("AzurAtt")	

func process(delta):
	if isItem or isDeath or sys.main.isAiStart == false:return 
	if isUpAtt:upAtt(false)
	if aiOn:_ai(delta)
	for i in range(buffs.size() - 1, - 1, - 1):
		if buffs.size() == 0:break
		if buffs.size() <= i:break
		var b = buffs[i]
		b.process(delta)
		if b.isDel:
			delBuff(b)
	for i in skills:
		i.nowTime += delta * (1 + att.cd)
		if i.nowTime >= i.cd and enableCastSkill:
			i.nowTime = 0
			sys.main.emit_signal("preCharaCastCdSkill", self, i)
			if i.enable:
				_castCdSkill(i.id)
				emit_signal("onCastCdSkill", i.id)
				sys.main.emit_signal("onCharaCastCdSkill", self, i.id)
				_onCharaCastCdSkill(self, i.id)
				newEff("xuLi2").setNorPos(sprcPos)
			else:
				i.enable = true
	time += delta
	if time >= 1:
		time -= 1
		if aiOn and enableCastSkill:
			_upS()
		upChaCell()
	hurtNum = 0
	if hitHua:
		hitHua = false
		newHitHua()

func addCdSkill(id, cd):
	var sk = AzurSkill.new()
	sk.cd = cd;
	sk.id = id
	skills.append(sk)

func _preCharaCastCdSkill(cha, sk):
	if not isItem:
		preCharaCastCdSkill(cha, sk)

func preCharaCastCdSkill(cha, sk):
	pass

func _onCharaDeathWord(cha):
	if not isItem:
		onCharaDeathWord(cha)
	
func onCharaDeathWord(cha):
	pass